package com.bigdata.spark.ml
import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.ml.feature.{HashingTF, Tokenizer}
import org.apache.spark.ml.linalg.Vector
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

/**
 * @author Gerry chan
 * @version 1.0
 * 2021/01/06 20:27
 * https://www.bilibili.com/video/BV1s441197ZB?p=71
 */
object PipelineExample {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession
      .builder()
      .master("local[*]")
      .appName("PipelineExample")
      .getOrCreate()

    val training = spark.createDataFrame(Seq(
      (0L, "a b c d e spark", 1.0),
      (1L, "b d", 0.0),
      (2L, "spark f g h", 1.0),
      (3L, "hadoop mapreduce", 0.0)
    )).toDF("id", "text", "label")

    val tokenizer = new Tokenizer()
      .setInputCol("text")
      .setOutputCol("words")

    val hashingTF = new HashingTF()
      .setNumFeatures(1000)
      .setInputCol(tokenizer.getOutputCol)
      .setOutputCol("features")

    val lr = new LogisticRegression()
      .setMaxIter(10)
      .setRegParam(0.001)
    //按照处理逻辑有序的组织PipelineStages,创建Pipeline
    //现在构建的Pipeline本质上是一个Estimator，在它的fit()方法运行之后，
    //它将产生一个PipelineModel,它是一个Transformer

    val pipeline = new Pipeline()
      .setStages(Array(tokenizer, hashingTF, lr))
    //可以看到，model的类型是一个PipelineModel,这个流水线模型将在测试数据的时候使用
    val model: PipelineModel = pipeline.fit(training)

    //保存模型
    model.write.overwrite().save("datas/spark-logistic-regression-model")

    //保存未训练的pipeline
    pipeline.write.overwrite().save("datas//unfit-lr-model")

    //加载模型
    val sameModel = PipelineModel.load("datas/spark-logistic-regression-model")

    //构建测试数据
    val test = spark.createDataFrame(Seq(
      (4L, "spark i j k"),
      (5L, "l m n"),
      (6L, "spark hadoop spark"),
      (7L, "apache hadoop")
    )).toDF("id", "text")

    //生成预测结果， probability表示概率，prediction:打标签的结果
    val frame: DataFrame = model.transform(test)
    frame.printSchema()

    frame.select("id", "text", "probability", "prediction")
        .collect()
        .foreach{
          case Row(id:Long,text:String, prob:Vector, prediction:Double) =>
            println(s"($id, $text) --> prob=$prob, prediction=$prediction")
        }

    spark.stop()
  }

}
